/*
 * Copyright (c) 2006 Intel Corporation
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached INTEL-LICENSE     
 * file. If you do not find these files, copies can be found by writing to
 * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
 * 94704.  Attention:  Intel License Inquiry.
 */

/**
 * 
 * @author David Gay
 * @author Kyle Jamieson
 */

#include "Timer.h"
#include "ConstrucaoInteligente.h"

module ConstrucaoInteligenteC @safe(){
  uses {
    // Interfaces for initialization:
    interface Boot;
    interface SplitControl as RadioControl;
    interface SplitControl as SerialControl;
    interface StdControl as RoutingControl;
	    
    // Interfaces for communication, multihop and serial:
    interface Send;
    interface Receive as Snoop;
    interface Receive;
    interface AMSend as SerialSend;
    interface CollectionPacket;
    interface RootControl;
	//ACK
    interface PacketAcknowledgements as ACKControl;
    interface Queue<message_t *> as UARTQueue;
    interface Pool<message_t> as UARTMessagePool;

	// controle de energia
    interface LowPowerListening;
    interface PacketField<uint8_t> as PacketTransmitPower;

    // Miscalleny:
    interface Timer<TMilli> as Timer0;
    interface Timer<TMilli> as Timer1;
    interface Timer<TMilli> as Timer2;
    interface Read<uint16_t> as Read0;
    interface Read<uint16_t> as Read1;
    interface Leds;

	
  }
}

implementation {
  task void uartSendTask();
  static void startTimer();
  static void stopTimer();
  static void started();
  static void stoped();
  static void fatal_problem();
  static void report_problem();
  static void report_sent();
  static void report_received();

  uint8_t uartlen;
  message_t sendbuf;
  message_t uartbuf;
  bool sendbusy=FALSE, uartbusy=FALSE;

  /* Current local state - interval, version and accumulated readings */
  pacoteci_t local;

  
  uint8_t reading0; /* 0 to NREADINGS */
  uint8_t reading1; /* 0 to NREADINGS */
  nx_uint16_t readings0[NREADINGS];
  nx_uint16_t readings1[NREADINGS];
  bool modo_extremo = FALSE;
  uint8_t contadorPerdaAck;
  uint8_t contadorAckRecebidos;
  uint8_t power;

  /* When we head an Oscilloscope message, we check it's sample count. If
     it's ahead of ours, we "jump" forwards (set our count to the received
     count). However, we must then suppress our next count increment. This
     is a very simple form of "time" synchronization (for an abstract
     notion of time). */
  bool suppress_count_change;

  // 
  // On bootup, initialize radio and serial communications, and our
  // own state variables.
  //
  event void Boot.booted() {
      local.id = TOS_NODE_ID;
      local.version = 0;
      started();
    
  }

  static void started(){
      // Beginning our initialization phases:
      if (call RadioControl.start() != SUCCESS)
          fatal_problem();

      if (call RoutingControl.start() != SUCCESS)
          fatal_problem();
  }

  static void stoped() {
   call Leds.led0Off();
   call Leds.led1Off();
   call Leds.led2Off();
    // Beginning our initialization phases:
   if (call RoutingControl.stop() != SUCCESS)
      fatal_problem(); 

   if (call RadioControl.stop() != SUCCESS)
      fatal_problem();
  }


  event void RadioControl.startDone(error_t error) {
		
      if (error != SUCCESS)
          fatal_problem();
      else
          call LowPowerListening.setLocalWakeupInterval(LPL_INTERVAL);		    	

      if (sizeof(local) > call Send.maxPayloadLength())
         fatal_problem();

    if (call SerialControl.start() != SUCCESS)
       fatal_problem();
  }

  event void SerialControl.startDone(error_t error) {
    if (error != SUCCESS)
      fatal_problem();

    // This is how to set yourself as a root to the collection layer:
    if (local.id == 0)
      call RootControl.setRoot();
    else
      startTimer();
  }

  static void startTimer() {
    if (call Timer0.isRunning())  
    {
       call Timer0.stop();
    }
    if (call Timer1.isRunning())  
    {
       call Timer1.stop();
    }
    reading0 = 0;
    reading1 = 0;
    contadorPerdaAck =0;
	contadorAckRecebidos =0;
    power=POWER_MINIMO;
    call Timer0.startPeriodic(DEFAULT_INTERVAL_LIGHTNESS);
    call Timer1.startPeriodic(DEFAULT_INTERVAL_TEMPERATURE);
	
  }

  static void stopTimer() {
    if (call Timer0.isRunning())  
    {
       call Timer0.stop();
    }
    if (call Timer1.isRunning())  
    {
       call Timer1.stop();
    }
	
  }




  event void RadioControl.stopDone(error_t error) { }
  event void SerialControl.stopDone(error_t error) { }

  //
  // Only the root will receive messages from this interface; its job
  // is to forward them to the serial uart for processing on the pc
  // connected to the sensor network.
  //
  event message_t*  Receive.receive(message_t* msg, void *payload, uint8_t len) {
    pacoteci_t* in = (pacoteci_t*)payload;
    pacoteci_t* out;
    if (uartbusy == FALSE) {
      out = (pacoteci_t*)call SerialSend.getPayload(&uartbuf, sizeof(pacoteci_t));
      if (len != sizeof(pacoteci_t) || out == NULL) {
	return msg;
      }
      else {
	memcpy(out, in, sizeof(pacoteci_t));
      }
      uartlen = sizeof(pacoteci_t);
      post uartSendTask();
    } else {
      // The UART is busy; queue up messages and service them when the
      // UART becomes free.
      message_t *newmsg = call UARTMessagePool.get();
      if (newmsg == NULL) {
        // drop the message on the floor if we run out of queue space.
        report_problem();
        return msg;
      }

      //Serial port busy, so enqueue.
      out = (pacoteci_t*)call SerialSend.getPayload(newmsg, sizeof(pacoteci_t));
      if (out == NULL) {
	return msg;
      }
      memcpy(out, in, sizeof(pacoteci_t));

      if (call UARTQueue.enqueue(newmsg) != SUCCESS) {
        // drop the message on the floor and hang if we run out of
        // queue space without running out of queue space first (this
        // should not occur).
        call UARTMessagePool.put(newmsg);
        fatal_problem();
        return msg;
      }
    }

    return msg;
  }

  task void uartSendTask() {
    if (call SerialSend.send(0xffff, &uartbuf, uartlen) != SUCCESS) {
      report_problem();
    } else {
      uartbusy = TRUE;
    }
  }

  event void SerialSend.sendDone(message_t *msg, error_t error) {
    	
	uartbusy = FALSE;
    if (call UARTQueue.empty() == FALSE) 
	{
      // We just finished a UART send, and the uart queue is
      // non-empty.  Let's start a new one.
      message_t *queuemsg = call UARTQueue.dequeue();
      if (queuemsg == NULL) 
	  {
        fatal_problem();
        return;
      }
      memcpy(&uartbuf, queuemsg, sizeof(message_t));
      if (call UARTMessagePool.put(queuemsg) != SUCCESS) 
	  {
        fatal_problem();
        return;
      }
      post uartSendTask();
    }
  }

  //
  // Overhearing other traffic in the network.
  //
  event message_t* 
  Snoop.receive(message_t* msg, void* payload, uint8_t len) {
    pacoteci_t *omsg = payload;

    report_received();
    //power
    call PacketTransmitPower.set(msg, power);	
    
    if (omsg->version > local.version) {
      local.version = omsg->version;
      startTimer();
    }

    // If we hear from a future count, jump ahead but suppress our own
    // change.
    if (omsg->count > local.count) {
      local.count = omsg->count;
      suppress_count_change = TRUE;
    }

    return msg;
  }

  /* At each sample period:
     - if local sample buffer is full, send accumulated samples
     - read next sample
  */
  event void Timer0.fired() {
    int i = 0;
    if (reading0 == NREADINGS) 
	{
      if (!sendbusy) 
	  {
	        pacoteci_t *o = (pacoteci_t *)call Send.getPayload(&sendbuf, sizeof(pacoteci_t));
			if (o == NULL) 
			{
			  fatal_problem();
			  return;
			}
			
			local.id_readings = ID_LIGHTNESS;
			for (i =0; i < NREADINGS;i++)				
				local.readings[i] = readings0[i];
			//local.interval = DEFAULT_INTERVAL_LIGHTNESS;
			local.power = power;
			memcpy(o, &local, sizeof(local));
			
			//power
			call PacketTransmitPower.set(&sendbuf, power);

			//ACK
			call ACKControl.requestAck(&sendbuf);
			
			if (call Send.send(&sendbuf, sizeof(local)) == SUCCESS)
			  sendbusy = TRUE;
			else
			  report_problem();
      }
      
      reading0 = 0;
      /* Part 2 of cheap "time sync": increment our count if we didn't
         jump ahead. */
      if (!suppress_count_change)
        local.count++;
      suppress_count_change = FALSE;
    }

    if (call Read0.read() != SUCCESS)
      fatal_problem();
  }

  event void Timer1.fired() {
    int i = 0;
    if (reading1 == NREADINGS) 
	{
		  if (!sendbusy) 
		  {
				pacoteci_t *o = (pacoteci_t *)call Send.getPayload(&sendbuf, sizeof(pacoteci_t));
				if (o == NULL) 
				{
				  fatal_problem();
				  return;
				}
				local.id_readings = ID_TEMPERATURE;
				for (i =0; i < NREADINGS;i++)				
					local.readings[i] = readings1[i];
				//local.interval = DEFAULT_INTERVAL_TEMPERATURE;
				local.power = power;
				memcpy(o, &local, sizeof(local));
				
				//power
				call PacketTransmitPower.set(&sendbuf, power);

				//ACK
			    call ACKControl.requestAck(&sendbuf);
				if (call Send.send(&sendbuf, sizeof(local)) == SUCCESS)
				  sendbusy = TRUE;
				else
				  report_problem();
		  }
		  
		  reading1 = 0;
		  /* Part 2 of cheap "time sync": increment our count if we didn't
			 jump ahead. */
		  if (!suppress_count_change)
			local.count++;
		  suppress_count_change = FALSE;
    }

		if (call Read1.read() != SUCCESS)
			fatal_problem();
  }

  event void Timer2.fired() {
	//started();
	startTimer();
	modo_extremo = TRUE;
	power = POWER_MAXIMO;
 }

 
  event void Send.sendDone(message_t* msg, error_t error) {
    
    //ACK
	if(call ACKControl.wasAcked(msg)) 
	{
	    modo_extremo = FALSE;
        contadorPerdaAck =0;	     
	    contadorAckRecebidos++;
	    call Leds.led0Off();
	    if (contadorAckRecebidos >LIMITE_ACK_ACEITOS)
	    {
		   if (power < POWER_MINIMO)
               power++;
	    }
	  
    }else if (modo_extremo){
		contadorPerdaAck++;
		contadorAckRecebidos=0;		
		if(contadorPerdaAck > LIMITE_PERDA_ACK_EXTREMO){
			stopTimer();
			//stoped();
			call Timer2.startOneShot(DEFAULT_SLEEP);
		}
	    	
	} 
	else 
	{
      // do something else if packet was not acked (possibly resend)
     	contadorPerdaAck++;
		contadorAckRecebidos=0;
		report_problem();
     	if (contadorPerdaAck >LIMITE_PERDA_ACK){
			if (power > POWER_MAXIMO){         
		    	power--;
			}else{
				stopTimer();
				//stoped();
				call Timer2.startOneShot(DEFAULT_SLEEP);
			}			
			contadorPerdaAck=0;        	
		}
    }    

    if (error == SUCCESS)
      report_sent();
    else
      report_problem();
    
	
    sendbusy = FALSE;
  }

  event void Read0.readDone(error_t result, uint16_t data) {
    if (result != SUCCESS) {
      data = 0xffff;
      report_problem();
    }
    if (reading0 < NREADINGS)
      readings0[reading0++] = data;
  }
  
  event void Read1.readDone(error_t result, uint16_t data) {
    if (result != SUCCESS) {
      data = 0xffff;
      report_problem();
    }
    if (reading1 < NREADINGS)
      readings1[reading1++] = data;
  }

  // Use LEDs to report various status issues.
  static void fatal_problem() { 
    call Leds.led0On(); 
    call Leds.led1On();
    call Leds.led2On();
    call Timer0.stop();
    call Timer1.stop();
  }

  static void report_problem() { call Leds.led0Toggle(); }
  static void report_sent() { call Leds.led1Toggle(); }
  static void report_received() { call Leds.led2Toggle(); }
}
